'''
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with self work for additional information
 * regarding copyright ownership.  The ASF licenses self file
 * to you under the Apache License, Version 2.0 (the
 * "License") you may not use self file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http:#www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''
from base.java.Iterator import Iterator

# import java.util.*;
# import java.util.function.Consumer;
# import java.util.function.Function;
# import java.util.function.Predicate;

'''
 * An ExtendedIterator is a:@link ClosableIterator on which other operations
 * are defined for convenience in iterator composition: composition, filtering
 * in, filtering out, and element mapping.<br>
 * NOTE that the result of these operations consumes the base iterator(s) they
 * do not make independent copies. <br>
 * The canonical implementation of ExtendedIterator is:@link NiceIterator,
 * which also defines static methods for these operations that will work on any
 *:@link ClosableIterators. <br>
 *
 * @param <T> the type of element over which an instance of ExtendedIterator
 *            iterates
'''
from abc import ABC, abstractmethod


class ExtendedIterator(Iterator):
    '''
     * Answer the next object, and remove it. Equivalent to next() remove().
    '''

    # param
    # result T
    @abstractmethod
    def removeNext(self):
        pass

    '''
     * return a new iterator which delivers all the elements of self iterator and
     * then all the elements of the other iterator. Does not copy either iterator
     * they are consumed as the result iterator is consumed.
    '''

    # param Iterator<X> other
    # result <X extends T> ExtendedIterator<T>
    @abstractmethod
    def andThen(self, other):
        pass

    '''
     * return a new iterator containing only the elements of _self_ which
     * pass the filter _f_. The order of the elements is preserved. Does not
     * copy _self_, which is consumed as the result is consumed.
    '''

    # param Predicate<T> f
    # result ExtendedIterator<T>
    @abstractmethod
    def filterKeep(self, f):
        pass

    '''
     * return a new iterator containing only the elements of _self_ which
     * are rejected by the filter _f_. The order of the elements is preserved.
     * Does not copy _self_, which is consumed as the result is consumed.
    '''

    # param Predicate<T> f
    # result ExtendedIterator<T>
    @abstractmethod
    def filterDrop(self, f):
        pass

    '''
     * return a new iterator where each element is the result of applying
     * _map1_ to the corresponding element of _self_. _self_ is not
     * copied it is consumed as the result is consumed.
    '''

    # param Function<T, U> map1
    # result <U> ExtendedIterator<U>
    @abstractmethod
    def mapWith(self, map1):
        pass

    '''
     * Execute an action on each element of the iterator.
     * This operation ends and closes the:@code ExtendedIterator even if there is an exception.
     * Shorter name for ":@code Iterator.forEachRemaining", adding exception handling.
    '''

    # param Consumer<T> action
    # result default void
    # @abstractmethod
    def forEach(self, action):
        try:
            while self.hasNext():
                action.accept(self.next())
        finally:
            self.close()

        # pass:
        # try:
        #     forEachRemaining(action)
        # finally:
        #     close()

    '''
     * Answer a list of the [remaining] elements of self iterator, in order,
     * consuming self iterator.
    '''

    @abstractmethod
    def close(self):
        pass

    # param
    # result List<T>
    @abstractmethod
    def toList(self):
        pass

    '''
     * Answer a set of the [remaining] elements of self iterator,
     * consuming self iterator.
    '''

    # param
    # result Set<T>
    @abstractmethod
    def toSet(self):
        pass

    '''
     * Answer with an:@link Optional.
     * This operation assumes that the:@code ExtendedIterator does not return null for:@code next().
     * If it does, :@code NullPointerException is thrown.
     * <ul>
     * <li>If there is no next, return:@code Optional.empty()
     * <li>If the next object exists, and is not null, return that in the:@link Optional.
     * <li>If the next object exists, and is null, throw:@code NullPointerException
     * </ul>
    '''


